package web

import (
	"awesomeProject/goAgentServer/db"
	"awesomeProject/goAgentServer/encryption"
	"encoding/base32"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

type WebInfo struct {
	Username string
	Password string
	Addr     string
}

type Users struct {
	Id         uint
	Username   string
	CreateDate int64
	UpdateDate int64
	LastHostID uint
	GroupIDs   string
	Remask     string
}

type HttpUserInfo struct {
	Code int
	Data []Users
}

type Hosts struct {
	Id         uint
	Addr       string
	Username   string
	CreateDate int64
	UpdateDate int64
	Remask     string
}

type HttpStatInfo struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

type HttpHostInfo struct {
	Code int
	Data []Hosts
}

type HttpGroupInfo struct {
	Code int
	Data []Groups
}

type Groups struct {
	Id         uint
	Names      string
	HostsIDs   string
	CreateDate int64
	UpdateDate int64
	Remask     string
}

var chars = []byte("abcdefghijklmnopqrstuvwxyz+=-_")
var w WebInfo

func init() {
	log.SetPrefix("web: ")
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}

func (wInfo *WebInfo) RunHttpServers() {
	w.Username = wInfo.Username
	w.Password = wInfo.Password

	//http.HandleFunc("/showPics", showPics)
	http.HandleFunc("/showHosts", showHosts)
	http.HandleFunc("/showGroups", showGroups)
	http.HandleFunc("/showUsers", showUsers)
	http.HandleFunc("/manageUsers", manageUsers)
	http.HandleFunc("/manageHosts", manageHosts)
	http.HandleFunc("/manageGroups", manageGroups)
	http.HandleFunc("/check", bashauth)
	http.Handle("/", http.FileServer(assetFS()))

	log.Println("start web server: ", wInfo.Addr)

	http.ListenAndServe(wInfo.Addr, nil)
}

func collectionAuthInfo(r *http.Request) bool {
	username, passwd, isok := r.BasicAuth()

	if isok && username == w.Username && passwd == w.Password {
		return true
	}
	return false
}

func bashauth(w http.ResponseWriter, r *http.Request) {
	if !collectionAuthInfo(r) {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"Administrator’s Area\"")
		w.WriteHeader(401)
		return
	}
}

func manageGroups(w http.ResponseWriter, r *http.Request) {

	if !collectionAuthInfo(r) {
		w.WriteHeader(401)
		return
	}

	groupName := r.PostFormValue("name")
	groupRemask := r.PostFormValue("remask")
	hostIds := r.PostFormValue("hostids")
	groupId := r.PostFormValue("delGroupID")

	var state HttpStatInfo

	err := db.ManageGroups(groupName, hostIds, groupRemask, groupId)
	if err != nil {
		state.Code = -1
		state.Msg = err.Error()

		stateJsonInfo, _ := json.Marshal(state)

		w.Write(stateJsonInfo)
		return
	}
	state.Code = 0
	state.Msg = "groups update ok"
	stateJsonInfo, _ := json.Marshal(state)
	w.Write(stateJsonInfo)
}

func manageHosts(w http.ResponseWriter, r *http.Request) {

	if !collectionAuthInfo(r) {
		w.WriteHeader(401)
		return
	}

	hostAddr := r.PostFormValue("addr")
	hostUsername := r.PostFormValue("hostname")
	password := r.PostFormValue("password")

	hostPassword := []byte{}
	var err error
	if password != "" {
		hostPassword, err = encryption.Encrypt(password)
		if err != nil {
			log.Println("encryption.Encrypt(password) error ", err)
			return
		}
	}

	keys := r.PostFormValue("identity")
	hostKey := []byte{}
	if keys != "" {
		hostKey, err = encryption.Encrypt(keys)
		if err != nil {
			log.Println("encryption.Encrypt(keys) error ", err)
			return
		}
	}
	hostRemask := r.PostFormValue("remask")
	delHostID := r.PostFormValue("delhostid")
	hostId := r.PostFormValue("id")

	var state HttpStatInfo

	if "" == delHostID {
		if hostId == "" {
			if hostAddr == "" || hostUsername == "" {
				log.Println("hosts add error , host addr or hostname is none")

				state.Code = -1
				state.Msg = "hosts add error , host addr or hostname is none"
				stateJsonInfo, _ := json.Marshal(state)

				w.Write(stateJsonInfo)
				return
			}
			err := db.ManageHosts(hostAddr, hostUsername, hostPassword, hostKey, hostRemask, -1, -1)
			if err != nil {
				log.Println("hosts add error ", err)

				state.Code = -1
				state.Msg = fmt.Sprintf("hosts add error , errors: %v", err)
				stateJsonInfo, _ := json.Marshal(state)

				w.Write(stateJsonInfo)
				return
			}

			state.Code = 0
			state.Msg = fmt.Sprintf("hosts add ok")
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)
			log.Println("hosts add ok , addr: ", hostAddr)
		} else {
			hostIdInt, err := strconv.Atoi(hostId)
			if err != nil {
				log.Println("strconv error ", err)

				state.Code = -1
				state.Msg = fmt.Sprintf("hosts update error , errors: %v", err)
				stateJsonInfo, _ := json.Marshal(state)

				w.Write(stateJsonInfo)

				return
			}

			err = db.ManageHosts(hostAddr, hostUsername, hostPassword, hostKey, hostRemask, -1, hostIdInt)
			if err != nil {
				log.Println("hosts add error ", err)

				state.Code = -1
				state.Msg = fmt.Sprintf("hosts update error , errors: %v", err)
				stateJsonInfo, _ := json.Marshal(state)

				w.Write(stateJsonInfo)
				return
			}

			state.Code = 0
			state.Msg = fmt.Sprintf("hosts update ok")
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)

			log.Println("hosts update ok , addr: ", hostAddr)
		}
	} else {
		delHostIDInt, err := strconv.Atoi(delHostID)
		if err != nil {
			log.Println("strconv error ", err)

			state.Code = -1
			state.Msg = fmt.Sprintf("hosts del error , errors: %v", err)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)

			return
		}

		err = db.ManageHosts(hostAddr, hostUsername, hostPassword, hostKey, hostRemask, delHostIDInt, -1)
		if err != nil {
			log.Println("hosts del error ", err)

			state.Code = -1
			state.Msg = fmt.Sprintf("hosts del error , errors: %v", err)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)

			return
		}
		state.Code = 0
		state.Msg = fmt.Sprintf("hosts del error , errors: %v", err)
		stateJsonInfo, _ := json.Marshal(state)

		w.Write(stateJsonInfo)
		log.Println("hosts delete ok , addr: ", hostAddr)
	}

}

func manageUsers(w http.ResponseWriter, r *http.Request) {

	if !collectionAuthInfo(r) {
		w.WriteHeader(401)
		return
	}

	userName := r.PostFormValue("username")
	userGroups := r.PostFormValue("group")
	userRemask := r.PostFormValue("remask")
	userId := r.PostFormValue("deluserid")

	var state HttpStatInfo

	if "" == userId {
		if "" == userName {
			log.Println("username is None")

			state.Code = -2
			state.Msg = fmt.Sprintf("addUsers failed , errors: %v", "username is none")
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)
			return
		}

		randPass := randPasswd()
		authCode := base32.StdEncoding.EncodeToString([]byte(randPass))
		userPassword, err := encryption.Encrypt(randPass)
		if err != nil {
			return
		}

		err, addFlag := db.ManageUsers(userName, userPassword, userGroups, userRemask, -1)
		if err != nil {
			log.Println("web manage users error ", err)
			state.Code = -2
			state.Msg = fmt.Sprintf("addUsers failed , errors: %v", err)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)
			return
		}

		if addFlag {
			state.Code = 5

			state.Msg = fmt.Sprintf("otpauth://totp/%s@agentServer?digits=6&period=30&secret=%s", userName, authCode)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)
		} else {
			state.Code = 1
			state.Msg = fmt.Sprintf("update User ok,userName: %s", userName)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)
		}

		//w.Write([]byte(fmt.Sprintf("{\"code\":\"0\",\"pics\":\"/showPics?picName=%s\",\"msg\":\"添加成功,请扫描图片二维码进行令牌注册.！若访问失败，可手动添加令牌信息: \"}", pngNmae, userName, randPass)))
	} else {
		userIdInt, err := strconv.Atoi(userId)
		if err != nil {
			log.Println("delUser error , error userId ", userId)

			state.Code = -2
			state.Msg = fmt.Sprintf("delUser error , error userId: %v", err)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)

			return
		}

		if userIdInt < 0 {
			log.Println("delUser error , error userId ", userId)

			state.Code = -2
			state.Msg = fmt.Sprintf("delUser error , error userId: %v", err)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)

			return
		}

		err, _ = db.ManageUsers("", []byte(""), "", "", userIdInt)
		if err != nil {
			log.Println("delUser error , error userId ", userId)

			state.Code = -2
			state.Msg = fmt.Sprintf("delUser error , error userId: %v", err)
			stateJsonInfo, _ := json.Marshal(state)

			w.Write(stateJsonInfo)
			return
		}

		state.Code = 0
		state.Msg = fmt.Sprintf("delUser ok , userId: %v", userId)
		stateJsonInfo, _ := json.Marshal(state)

		w.Write(stateJsonInfo)
	}
}

//func showPics(w http.ResponseWriter, r *http.Request) {
//	picNmae := r.FormValue("picName")
//
//	datas, err := ioutil.ReadFile(picNmae)
//	if err != nil {
//		w.Write([]byte("error code: 404"))
//		return
//	}
//	w.Write(datas)
//
//	err = os.Remove(picNmae)
//	if err != nil {
//		log.Println("showPics remove files error", err)
//	}
//}

func showGroups(w http.ResponseWriter, r *http.Request) {

	if !collectionAuthInfo(r) {
		w.WriteHeader(401)
		return
	}

	var groups HttpGroupInfo

	groupid := r.FormValue("groupid")

	if "" == groupid {
		group := db.QueryGroupsAllInfo()

		for _, v := range group {
			var groupInfo Groups
			groupInfo.Id = v.Id
			groupInfo.Names = v.Names
			groupInfo.HostsIDs = v.HostsIDs
			groupInfo.Remask = v.Remask
			groupInfo.CreateDate = v.CreateDate
			groupInfo.UpdateDate = v.UpdateDate

			groups.Data = append(groups.Data, groupInfo)
		}
	} else {
		group, err := db.QueryGroupInfo(groupid)
		if err != nil {
			log.Println("query groups error ", err)
		}
		var groupInfo Groups
		groupInfo.Id = group.Id
		groupInfo.Names = group.Names
		groupInfo.HostsIDs = group.HostsIDs
		groupInfo.Remask = group.Remask
		groupInfo.CreateDate = group.CreateDate
		groupInfo.UpdateDate = group.UpdateDate

		groups.Data = append(groups.Data, groupInfo)
	}

	dataByte, err := json.Marshal(groups)
	if err != nil {
		log.Println("show groups marshal error ", err)
		w.Write([]byte("show groups error"))
	}
	w.Write(dataByte)
}

func showHosts(w http.ResponseWriter, r *http.Request) {

	if !collectionAuthInfo(r) {
		w.WriteHeader(401)
		return
	}

	hostid := r.FormValue("id")

	var hosts HttpHostInfo

	if hostid == "" {
		host := db.QueryHostsAllInfo()

		for _, v := range host {
			var hostInfo Hosts
			hostInfo.Id = v.Id
			hostInfo.Addr = v.Addr
			hostInfo.Username = v.Hostname
			hostInfo.Remask = v.Remask
			hostInfo.CreateDate = v.CreateDate
			hostInfo.UpdateDate = v.UpdateDate

			hosts.Data = append(hosts.Data, hostInfo)
		}
	} else {
		host, err := db.QueryHostsInfo(hostid)
		if err != nil {
			log.Println("query host info error ", err)
		}
		var hostInfo Hosts
		hostInfo.Id = host.Id
		hostInfo.Addr = host.Addr
		hostInfo.Username = host.Hostname
		hostInfo.Remask = host.Remask
		hostInfo.CreateDate = host.CreateDate
		hostInfo.UpdateDate = host.UpdateDate

		hosts.Data = append(hosts.Data, hostInfo)
	}

	dataByte, err := json.Marshal(hosts)
	if err != nil {
		log.Println("show hosts marshal error ", err)
		w.Write([]byte("show hosts error"))
	}
	w.Write(dataByte)
}

func showUsers(w http.ResponseWriter, r *http.Request) {

	if !collectionAuthInfo(r) {
		w.WriteHeader(401)
		return
	}

	username := r.FormValue("username")

	var users HttpUserInfo

	if "" == username {
		user := db.QueryUserAllInfo()

		for _, v := range user {
			var userInfo Users
			userInfo.Id = v.ID
			userInfo.Username = v.Username
			userInfo.CreateDate = v.CreateDate
			userInfo.UpdateDate = v.UpdateDate
			userInfo.Remask = v.Remask
			userInfo.GroupIDs = v.GroupIDs
			userInfo.LastHostID = v.LastHostID

			users.Data = append(users.Data, userInfo)
		}
	} else {
		user := db.QueryUserInfo(username)
		var userInfo Users
		userInfo.Id = user.ID
		userInfo.Username = user.Username
		userInfo.CreateDate = user.CreateDate
		userInfo.UpdateDate = user.UpdateDate
		userInfo.Remask = user.Remask
		userInfo.GroupIDs = user.GroupIDs
		userInfo.LastHostID = user.LastHostID

		users.Data = append(users.Data, userInfo)
	}

	dataByte, err := json.Marshal(users)
	if err != nil {
		log.Println("show users marshal error ", err)
		w.Write([]byte("show users error"))
	}
	w.Write(dataByte)
}

func randPasswd() string {
	rand.Seed(time.Now().UnixNano())

	b := make([]byte, 20)
	for i := range b {
		b[i] = chars[rand.Intn(len(chars))]
	}
	return string(b)
}
